package model

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/fogleman/gg"
	dsp "github.com/mjibson/go-dsp/fft"
	"image/color"
	"io"
	"math"
	"os"
	"os/exec"
	"sort"
	"strconv"
	"strings"
)

type AudioStat struct {
	Second        int         `json:"second"`
	PeakLevelDB   float64     `json:"peak_level_db"` //Peak level dB 峰值音量（相对于 0dBFS），越接近 0 越响亮
	RMSLevelDB    float64     `json:"rms_level_db"`  //均方根电平（平均响度），用来判断响度情况
	RMSPeakDB     float64     `json:"rms_peak_db"`   //RMS 所达到的最大值（不是瞬时峰）
	RMSTroughDB   float64     `json:"rms_trough_db"` //RMS 最低谷值
	CrestFactor   float64     `json:"crest_factor"`  //峰均比（Peak / RMS），判断信号爆发力，越大表明冲击成分越强
	EstSNR        float64     `json:"est_snr"`       //信噪比（需开启 measure_overall=1）	< 1 dB 近似无信号
	NoWater       bool        `json:"no_water"`
	LeakDetected  bool        `json:"leak_detected"`   //漏水判定逻辑（简易规则）： 满足以下三条件即视为“可能漏水声音”： 高频占比 > 0.3 SNR > 5.0 dB 非静音（RMS dB 未低至 -70）
	FreqSpectrum  []FreqPoint `json:"freq_spectrum"`   // 频率对应幅值` // 新增频谱数组，实部模长
	HighFreqRatio float64     `json:"high_freq_ratio"` //高频占比
}
type FreqPoint struct {
	// FreqHz represents the frequency of the signal in hertz.
	// This is the number of cycles per second of the signal.
	FreqHz float64 `json:"freq_hz"`

	// Value represents the processed value of the signal.
	// This value is obtained after the signal has been processed by specific algorithms or filters.
	Value float64 `json:"value"`

	// RawValue represents the raw, unprocessed value of the signal.
	// This is the value directly obtained from the signal source, without any processing or filtering.
	RawValue float64 `json:"raw_value"`
}

const (
	sampleRate      = 8000                               // Hz
	channels        = 1                                  // mono
	sampleSize      = 2                                  // 16-bit = 2 bytes
	secBytes        = sampleRate * channels * sampleSize // 每秒字节数 = 8000 × 1 × 2 = 16000
	silenceRMSRefDB = -80.0                              // 可根据样本调整
	freqBinSizeHz   = 10.0                               // 可改为任意精度
)

var allSpectrumMap = make(map[float64][]float64) // 合成频谱绘图使用
func dbToLinear(db float64) float64 {
	return math.Pow(10, db/20)
}
func complexAbs(c complex128) float64 {
	return math.Sqrt(real(c)*real(c) + imag(c)*imag(c))
}
func linearToDB(val float64) float64 {
	if val <= 0 {
		return -999.0
	}
	return 20 * math.Log10(val)
}
func drawCombinedSpectrum() {
	if len(allSpectrumMap) == 0 {
		return
	}

	// 增加图像尺寸
	const width, height = 1200, 800
	const margin = 80 // 增加边距，为Y轴标签留出更多空间

	dc := gg.NewContext(width+margin*2, height+margin*2)

	// 设置白色背景
	dc.SetRGB(1, 1, 1)
	dc.Clear()

	var frequency []float64
	for f := range allSpectrumMap {
		frequency = append(frequency, f)
	}
	sort.Float64s(frequency)
	maxFreq := frequency[len(frequency)-1]

	// 绘制坐标轴
	dc.SetLineWidth(2)
	dc.SetColor(color.Black)
	// X轴
	dc.DrawLine(margin, height+margin, width+margin, height+margin)
	dc.Stroke()
	// Y轴
	dc.DrawLine(margin, margin, margin, height+margin)
	dc.Stroke()

	// 绘制X轴刻度和标签
	dc.SetLineWidth(1)
	for i := 0; i <= 10; i++ {
		x := margin + float64(i)*width/10
		// 刻度线
		dc.DrawLine(x, height+margin, x, height+margin+10)
		dc.Stroke()
		// 标签
		freqValue := int(maxFreq * float64(i) / 10)
		dc.DrawStringAnchored(fmt.Sprintf("%d Hz", freqValue), x, height+margin+30, 0.5, 0)
	}

	// 绘制Y轴刻度和标签 - 使用dB单位，更符合音频工程习惯
	dc.SetColor(color.Black)
	for i := 0; i <= 10; i++ {
		y := height + margin - float64(i)*height/10
		// 刻度线
		dc.SetLineWidth(1)
		dc.DrawLine(margin-10, y, margin, y)
		dc.Stroke()
		// 标签 - 显示dB值，从-60dB到0dB
		dbValue := -60.0 + float64(i)*6.0 // -60dB 到 0dB，每格6dB
		dc.DrawStringAnchored(fmt.Sprintf("%.0f", dbValue), margin-35, y, 1, 0.5)
	}

	// 绘制坐标轴标题 - 改进字体大小和位置
	dc.SetColor(color.Black)
	dc.DrawStringAnchored("频率 (Hz)", width/2+margin, height+margin+50, 0.5, 0)

	// Y轴标题 - 使用专业音频术语
	dc.RotateAbout(-math.Pi/2, margin-55, height/2+margin)
	dc.DrawStringAnchored("幅度 (dB)", margin-55, height/2+margin, 0.5, 0)
	dc.RotateAbout(math.Pi/2, margin-55, height/2+margin)

	// 绘制漏水高能频段背景（1000Hz-3000Hz）
	leakStartX := margin + float64(width)*1000/maxFreq
	leakEndX := margin + float64(width)*3000/maxFreq
	if leakEndX > width+margin {
		leakEndX = width + margin
	}

	// 使用淡黄色标记漏水高能频段区域
	dc.SetRGBA(1, 1, 0, 0.2)
	dc.DrawRectangle(leakStartX, margin, leakEndX-leakStartX, height)
	dc.Fill()

	// 绘制频谱线 - 使用dB刻度
	dc.SetLineWidth(1.5)
	for _, f := range frequency {
		values := allSpectrumMap[f]
		avg := 0.0
		for _, v := range values {
			avg += v
		}
		avg /= float64(len(values))

		x := margin + float64(width)*f/maxFreq

		// 将归一化值转换为dB值进行显示 (-60dB 到 0dB)
		var dbValue float64
		if avg > 0 {
			dbValue = 20 * math.Log10(avg) // 转换为dB
			if dbValue < -60 {
				dbValue = -60 // 限制最小值为-60dB
			}
		} else {
			dbValue = -60 // 静音时设为-60dB
		}

		// 将dB值映射到图表高度 (-60dB=底部, 0dB=顶部)
		dbNormalized := (dbValue + 60) / 60 // 将-60到0映射到0到1
		y := height + margin - (dbNormalized * float64(height))

		// 如果在漏水高能频段内，使用红色
		if f >= 1000 && f <= 3000 {
			dc.SetColor(color.RGBA{R: 255, G: 0, B: 0, A: 255})
		} else {
			dc.SetColor(color.RGBA{R: 0, G: 0, B: 255, A: 255})
		}

		dc.DrawLine(x, height+margin, x, y)
		dc.Stroke()
	}

	// 添加图例
	legendX := float64(margin + 50)
	legendY := float64(margin + 50)

	// 红色线图例
	dc.SetColor(color.RGBA{R: 255, G: 0, B: 0, A: 255})
	dc.DrawLine(legendX, legendY, legendX+30, legendY)
	dc.Stroke()
	dc.SetColor(color.Black)
	dc.DrawStringAnchored("漏水高能频段 (1000Hz-3000Hz)", legendX+40, legendY, 0, 0.5)

	// 蓝色线图例
	dc.SetColor(color.RGBA{R: 0, G: 0, B: 255, A: 255})
	dc.DrawLine(legendX, legendY+30, legendX+30, legendY+30)
	dc.Stroke()
	dc.SetColor(color.Black)
	dc.DrawStringAnchored("其他频段", legendX+40, legendY+30, 0, 0.5)

	// 添加标题
	dc.SetColor(color.Black)
	dc.DrawStringAnchored("音频频谱分析", width/2+margin, margin-30, 0.5, 0.5)

	// 添加Y轴数值说明 - 使用专业音频术语
	dc.SetColor(color.RGBA{R: 0, G: 0, B: 0, A: 200}) // 稍微透明的黑色
	dc.DrawStringAnchored("Y轴: 幅度 (-60dB 到 0dB)", margin+50, margin-10, 0, 0.5)
	dc.DrawStringAnchored("• 0dB = 最大幅度  • -60dB = 最小幅度", margin+50, margin+10, 0, 0.5)

	// 创建目录并保存图像
	err := os.MkdirAll("spectrums", 0755)
	if err != nil {
		return
	}
	err = dc.SavePNG("spectrums/spectrum_combined.png")
	if err != nil {
		return
	}
}

func extractStats(dumpfile string, pcm []byte, second int) (*AudioStat, error) {
	err := os.WriteFile(dumpfile, pcm, 0644)
	if err != nil {
		return nil, err
	}

	cmd := exec.Command("ffmpeg",
		"-f", "s16le",
		"-ar", strconv.Itoa(sampleRate),
		"-ac", strconv.Itoa(channels),
		"-i", dumpfile,
		"-af", "astats=metadata=1:reset=1:measure_overall=1",
		"-f", "null", "-")

	var out bytes.Buffer
	cmd.Stderr = &out
	err = cmd.Run()
	if err != nil {
		return nil, err
	}

	output := out.String()
	stat := &AudioStat{Second: second}
	parseValue := func(key string) float64 {
		for _, line := range strings.Split(output, "\n") {
			if strings.Contains(line, key) {
				parts := strings.Fields(line)
				val, _ := strconv.ParseFloat(parts[len(parts)-1], 64)
				return val
			}
		}
		return -999.0
	}

	stat.RMSLevelDB = parseValue("RMS level dB")
	stat.PeakLevelDB = parseValue("Peak level dB")
	stat.RMSPeakDB = parseValue("RMS peak dB")
	stat.RMSTroughDB = parseValue("RMS trough dB")
	stat.CrestFactor = parseValue("Crest factor")

	snrVal := parseValue("SNR")
	if snrVal != -999.0 {
		stat.EstSNR = snrVal
	} else {
		linearRMS := dbToLinear(stat.RMSLevelDB)
		linearNoise := dbToLinear(silenceRMSRefDB)
		if linearNoise > 0 {
			stat.EstSNR = linearToDB(linearRMS / linearNoise)
		} else {
			stat.EstSNR = -999.0
		}
	}

	if stat.RMSLevelDB < -70.0 && stat.EstSNR < 1.0 {
		stat.NoWater = true
	}
	// 🔍 提取频谱（只保留模长）
	samples := make([]float64, len(pcm)/2)
	for i := 0; i < len(pcm); i += 2 {
		sample := int16(pcm[i]) | int16(pcm[i+1])<<8
		samples[i/2] = float64(sample)
	}
	spectrum := dsp.FFTReal(samples)
	binSize := float64(sampleRate) / float64(len(samples))
	binEnergy := make(map[int]float64)
	var totalEnergy, highFreqEnergy, maxEnergy float64

	for i := 0; i < len(spectrum)/2; i++ {
		freq := float64(i) * binSize
		mag := complexAbs(spectrum[i])
		bin := int(freq/freqBinSizeHz) * int(freqBinSizeHz)
		binEnergy[bin] += mag
		totalEnergy += mag
		if freq >= 1000 && freq <= 3000 {
			highFreqEnergy += mag
		}
		if binEnergy[bin] > maxEnergy {
			maxEnergy = binEnergy[bin]
		}
	}
	var points []FreqPoint
	for bin, val := range binEnergy {
		normVal := 0.0
		if maxEnergy > 0 {
			normVal = val / maxEnergy
		}
		points = append(points, FreqPoint{
			FreqHz:   float64(bin),
			Value:    normVal,
			RawValue: val,
		})
	}
	// 合成频谱数据聚合（每秒所有 bin 合并）
	for _, pt := range points {
		allSpectrumMap[pt.FreqHz] = append(allSpectrumMap[pt.FreqHz], pt.Value)
	}

	// 绘制单秒频谱图
	if len(points) > 0 {
		// 增加图像尺寸，使图像更长
		const width, height = 1600, 800
		const margin = 80    // 增加边距，为Y轴标签留出更多空间
		const barWidth = 3.0 // 柱状图宽度

		dc := gg.NewContext(width+margin*2, height+margin*2)
		dc.SetRGB(1, 1, 1)
		dc.Clear()

		// 绘制坐标轴
		dc.SetLineWidth(2)
		dc.SetColor(color.Black)
		// X轴
		dc.DrawLine(margin, height+margin, width+margin, height+margin)
		dc.Stroke()
		// Y轴
		dc.DrawLine(margin, margin, margin, height+margin)
		dc.Stroke()

		// 绘制X轴刻度和标签
		dc.SetLineWidth(1)
		maxFreq := points[len(points)-1].FreqHz
		for i := 0; i <= 10; i++ {
			x := margin + float64(i)*width/10
			// 刻度线
			dc.DrawLine(x, height+margin, x, height+margin+10)
			dc.Stroke()
			// 标签
			freqValue := int(maxFreq * float64(i) / 10)
			dc.DrawStringAnchored(fmt.Sprintf("%d Hz", freqValue), x, height+margin+30, 0.5, 0)
		}

		// 绘制Y轴刻度和标签 - 使用dB单位
		dc.SetColor(color.Black)
		for i := 0; i <= 10; i++ {
			y := height + margin - float64(i)*height/10
			// 刻度线
			dc.SetLineWidth(1)
			dc.DrawLine(margin-10, y, margin, y)
			dc.Stroke()
			// 标签 - 显示dB值，从-60dB到0dB
			dbValue := -60.0 + float64(i)*6.0 // -60dB 到 0dB，每格6dB
			dc.DrawStringAnchored(fmt.Sprintf("%.0f", dbValue), margin-35, y, 1, 0.5)
		}

		// 绘制坐标轴标题 - 改进字体大小和位置
		dc.SetColor(color.Black)
		dc.DrawStringAnchored("频率 (Hz)", width/2+margin, height+margin+50, 0.5, 0)

		// Y轴标题 - 使用专业音频术语
		dc.RotateAbout(-math.Pi/2, margin-55, height/2+margin)
		dc.DrawStringAnchored("幅度 (dB)", margin-55, height/2+margin, 0.5, 0)
		dc.RotateAbout(math.Pi/2, margin-55, height/2+margin)

		// 绘制漏水高能频段背景（1000Hz-3000Hz）
		leakStartX := margin + float64(width)*1000/maxFreq
		leakEndX := margin + float64(width)*3000/maxFreq
		if leakEndX > width+margin {
			leakEndX = width + margin
		}

		// 使用淡黄色标记漏水高能频段区域
		dc.SetRGBA(1, 1, 0, 0.2)
		dc.DrawRectangle(leakStartX, margin, leakEndX-leakStartX, height)
		dc.Fill()

		// 绘制频谱柱状图 - 使用dB刻度
		for _, pt := range points {
			x := margin + float64(width)*pt.FreqHz/maxFreq

			// 将归一化值转换为dB值进行显示 (-60dB 到 0dB)
			var dbValue float64
			if pt.Value > 0 {
				dbValue = 20 * math.Log10(pt.Value) // 转换为dB
				if dbValue < -60 {
					dbValue = -60 // 限制最小值为-60dB
				}
			} else {
				dbValue = -60 // 静音时设为-60dB
			}

			// 将dB值映射到图表高度 (-60dB=0高度, 0dB=最大高度)
			dbNormalized := (dbValue + 60) / 60 // 将-60到0映射到0到1
			barHeight := dbNormalized * float64(height)

			// 如果在漏水高能频段内，使用红色
			if pt.FreqHz >= 1000 && pt.FreqHz <= 3000 {
				dc.SetColor(color.RGBA{R: 255, G: 0, B: 0, A: 255})
			} else {
				dc.SetColor(color.RGBA{R: 0, G: 0, B: 255, A: 255})
			}

			// 绘制柱状图而不是线
			dc.DrawRectangle(x-barWidth/2, height+margin-barHeight, barWidth, barHeight)
			dc.Fill()
		}

		// 添加标题
		dc.SetColor(color.Black)
		dc.DrawStringAnchored(fmt.Sprintf("第 %d 秒音频频谱分析", second), width/2+margin, margin-30, 0.5, 0.5)

		// 添加Y轴数值说明 - 使用专业音频术语
		dc.SetColor(color.RGBA{R: 0, G: 0, B: 0, A: 200}) // 稍微透明的黑色
		dc.DrawStringAnchored("Y轴: 幅度 (-60dB 到 0dB)", margin+50, margin-10, 0, 0.5)
		dc.DrawStringAnchored("• 0dB = 最大幅度  • -60dB = 最小幅度", margin+50, margin+10, 0, 0.5)

		os.MkdirAll("spectrums", 0755)
		dc.SavePNG(fmt.Sprintf("spectrums/spectrum_second_%d.png", second))
	}
	if len(stat.FreqSpectrum) == 0 {
		fmt.Printf("警告：第 %d 秒无有效频谱，跳过图像绘制\\n", second)
	}
	sort.Slice(points, func(i, j int) bool {
		return points[i].FreqHz < points[j].FreqHz
	})

	stat.FreqSpectrum = points
	if totalEnergy > 0 {
		stat.HighFreqRatio = highFreqEnergy / totalEnergy
	}

	if stat.HighFreqRatio > 0.3 && stat.EstSNR > 5.0 && !stat.NoWater {
		stat.LeakDetected = true
	}
	//log.Printf("分析文件%s结果 %v\n", dumpfile, *stat)
	return stat, nil
}

func (p *AudioStat) AnalyzePCM(file string) ([]AudioStat, error) {
	f, err := os.Open(file)
	if err != nil {
		return nil, err
	}
	defer func(f *os.File) {
		err := f.Close()
		if err != nil {

		}
	}(f)

	var results []AudioStat
	buf := make([]byte, secBytes)
	second := 0
	// 创建临时文件
	tmpFile, err := os.CreateTemp("", "extractStats-*")
	if err != nil {
		return nil, fmt.Errorf("failed to create temp file: %w", err)
	}
	// 关闭临时文件
	err = tmpFile.Close()
	if err != nil {
		return nil, fmt.Errorf("failed to close temp file: %w", err)
	}
	for {
		n, err := io.ReadFull(f, buf)
		if err == io.EOF || errors.Is(err, io.ErrUnexpectedEOF) {
			break
		}
		if err != nil {
			return nil, err
		}
		if n == secBytes {
			stat, err := extractStats(tmpFile.Name(), buf, second)
			if err != nil {
				return nil, err
			}
			results = append(results, *stat)
			second++
		}
	}

	// 分析完成后绘制合成频谱图
	drawCombinedSpectrum()

	// 删除临时文件
	defer func(name string) {
		if err := os.Remove(name); err != nil {
		}
	}(tmpFile.Name())

	return results, nil
}
